#!/usr/bin/env python3
#  -*- coding: utf-8 -*-
#  Copyright 2019 NetworkRADIUS SARL (legal@networkradius.com)
#  This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
#
#  Author: Jorge Pereira <jpereira@freeradius.org>
#  Version $Id: bcf93dac312ae50a77363348881c9ca8cf8ae4cb $
#

#
#  EXAMPLE:
#
#  1. wget https://github.com/the-tcpdump-group/tcpdump/raw/master/tests/dhcpv6-sip-server-d.pcap
#  2. ./scripts/pcap2decode-proto.py -p dhcpv6 -f ./dhcpv6-sip-server-d.pcap > src/tests/unit/protocols/dhcpv6/packet_sip-server-d.txt
#  3. build/make/jlibtool --quiet --mode=execute build/bin/local/unit_test_attribute -xx -D share/dictionary src/tests/unit/protocols/dhcpv6/packet_sip-server-d.txt
#
#  TODO:
#
#  - verify if the jlibtool and unit_test_attribute exist
#  - check if we could call dhcpv6_tp_decode_pair direct from libfreeradius-dhcpv6.{so,dylib}
#

from __future__ import print_function
import argparse
import tempfile
import traceback
import sys
import os
import re

unit_attr = "build/make/jlibtool --quiet --mode=execute " \
            "build/bin/local/unit_test_attribute -xx "    \
            "-D share/dictionary "                        \
            "-d src/tests/unit"

# print to stderr
def eprint(*args, **kwargs):
	print(*args, file=sys.stderr, **kwargs)

try:
	from scapy.all import *
except Exception as e:
	eprint("** ERROR: We need the 'scapy' package. e.g: pip3 install scapy")
	sys.exit(-1)

# It does like: unit_test_attribute ... /path/file.txt | sed '/got.*:/!d; s/.\{2\}/& /g; s/ $//g'
def unit_lookup_payload2attrs(proto, payload):
	# Generating lookup file
	fp = tempfile.NamedTemporaryFile(mode = "w+", delete = False)
	fp.write("# Using {}\n".format(fp.name))
	fp.write("proto {}\n".format(proto))
	fp.write("proto-dictionary {}\n".format(proto))
	fp.write("\n")
	fp.write("decode-proto {}\n".format(payload))
	fp.write("match Packet-Type = 1\n")
	fp.flush()
	fp.close()

	# call the unit_test_attribute
	cmd_unit = "{} {}".format(unit_attr, fp.name)
	cmd_out  = os.popen(cmd_unit).read()
	os.remove(fp.name)

	match = re.search(r"(.\sgot\s+:)\s(.*)", cmd_out)
	if not match:
		eprint("# ERROR: We didn't find the 'got' token in: {}".format(cmd_out))
		return None

	return match.group(2)

def load_args():
	parser = argparse.ArgumentParser(
		description = "Convert .pcap file to FreeRADIUS unit_test_attribute(encode/decode) format. {almost, try}",
	)
	parser.add_argument(
		"-f",
		dest = "pcap_file",
		help = "pcap file to extract the dhcpv6 payload",
		required = True
	)
	parser.add_argument(
		"-p",
		dest = "decode_proto",
		help = "Protocol to be used in: 'proto $proto' and 'proto-dictionary $proto'",
		required = True
	)
	parser.add_argument(
		"-b",
		dest = "both",
		help = "Perform the lookup adding the 'encode-proto attrs' and 'decode_proto -'",
		action='store_true'
	)
	return parser.parse_args()

def _main():
	try:
		args      = load_args()
		count_pkt = 0
		count_mat = 0
		pcap      = rdpcap(args.pcap_file)

		print("#  -*- text -*-")
		print("#  ATTENTION: It was generated automatically, be careful! :)")
		print("#  Based on {}".format(os.path.basename(args.pcap_file)))
		print("#")
		print("")
		print("proto {}".format(args.decode_proto))
		print("proto-dictionary {}".format(args.decode_proto))
		print("")
		count_mat += 2

		for pkt in pcap:
			# statements
			count_pkt += 1
			print("#")
			print("#  {}.".format(count_pkt))
			print("#")

			# get the payload description, remove '#' and trim() spaces.
			app = pkt.getlayer(3)
			packet_desc = app.show(dump=True, indent=1).replace("#", "")
			packet_desc = re.sub('^', "# ", packet_desc, flags=re.MULTILINE)
			packet_desc = re.sub(' $', "",  packet_desc, flags=re.MULTILINE)
			print(packet_desc.strip())

			# Convert the payload to hex separated by space.
			payload = ""
			for d in app.build():
				payload += "{:02x} ".format(d)

			# trim the left/right
			payload = payload.strip()

			# lookup the attrs from the payload
			attrs = unit_lookup_payload2attrs(args.decode_proto, payload)
			if not attrs:
				raise Exception("Error", "Problems to convert the payload to attrs")

			if args.both:
				count_mat += 4
				print("encode-proto {}".format(attrs))
				print("match {}".format(payload))
				print("")
				print("decode-proto -")
				print("match {}".format(attrs))
				print("")
			else:
				count_mat += 2
				print("decode-proto {}".format(payload))
				print("match {}".format(attrs))
				print("")

		# append the 'count'
		print("count")
		print("match {}".format(count_mat))
		print("")

	except Exception as e:
		eprint("** ERROR: Something wrong:\n {}\n".format(str(e)))
		traceback.print_exc()
		sys.exit(-1)

if __name__ == "__main__":
	_main()
